home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.04 Apr 95 / Performance / Fractal 8 / FractalEngine.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-13  |  16.0 KB  |  646 lines  |  [TEXT/MPS ]

  1. /*    
  2.     File:        FractalEngine.c
  3.  
  4.     Used to build:    “Fractal 8”
  5.     
  6.     Written by:        Jim Cathey            July 1985
  7.                     Eric Traut            November 1994
  8.  
  9.     Description:
  10.         The following code implements a “Fractal Contour” generating
  11.         program. See comments in the file “FractalMain.c” for more
  12.         information.
  13.         
  14.         The code in this file implements the fractal-generating
  15.         and plotting portion of the program.
  16. */
  17.  
  18.  
  19. #include <Quickdraw.h>
  20. #include <ToolUtils.h>
  21. #include <Timer.h>
  22.  
  23. #include <stdlib.h>
  24.  
  25. #include "Fractal.h"
  26.  
  27. /* Functions defined within this file */
  28. static double RandomDouble(void);
  29. static void CalcXs(long len, long incr, long sk);
  30. static void CalcYs(long len, long incr, long sk);
  31. static void CalcDiagonals(long len, long incr, long sk);
  32. static long GetZValue(long x, long y);
  33. static long GetOldZValue(long x, long y);
  34. static void SetZValue(long d, long x, long y);
  35. static unsigned char GetLineColor(unsigned short red, unsigned short blue, unsigned short green);
  36. static void LineToOffscreen(long sX, long sY, long eX, long eY);
  37. static void LineXOffscreen(long startX, long startY, long endX, long endY);
  38. static void LineYOffscreen(long startX, long startY, long endX, long endY);
  39. static void PointOffscreen(long x, long y , unsigned char colorValue);
  40. static void SetGrayValues(void);
  41. static void Transform1Point(long xIndex, long yIndex, ThreePoint *resultPoint);
  42. static void WaterPlotLine(ThreePoint *startPoint, ThreePoint *endPoint);
  43.  
  44. /* Global variables */
  45. long             gMaxX, gMaxY;
  46. long            gLevel;                    /* Current level */
  47. unsigned char    gCurPixelColor;            /* Pixel value for offscreen line drawing */
  48. unsigned char    gDBluePixelColor;
  49. unsigned char    gLBluePixelColor;
  50. unsigned char    gBlackPixelColor;
  51. char*            gPixelBase;                /* Base of pixel store */
  52. long            gRowBytesIncrement;        /* Amount to increment each offscreen row */
  53. char            gGrayColors[kTotalGrays];/* Gray color table */
  54.  
  55. /* Macros */
  56. #define AbsoluteValue(v) ((v) >= 0 ? (v) : -(v))
  57. #define ConvertToShadeIndex(x) ((x >= kShadeMapSize) ? (kShadeMapSize - 1) : x)
  58.  
  59.  
  60. /*
  61.     RandomDouble
  62.     
  63.     This function returns a random number between -1 and 1.
  64. */
  65. double RandomDouble(void)
  66. {
  67.     return (double)rand() / RAND_MAX;
  68. }
  69.  
  70.  
  71. /*
  72.     CalcSurface
  73.     
  74.     This is the main surface-generating routine. It generates a random
  75.     fractal surface by filling in the gPointArray array.
  76. */
  77. void CalcSurface(unsigned short level)
  78. {
  79.     long             i, j, length, incrby, sk;
  80.     float             power;
  81.     UnsignedWide    startTime, endTime;
  82.  
  83.     if (gMorphFractal)
  84.         gMorphAmount = 0.0;
  85.     else
  86.         gMorphAmount = 1.0;
  87.     gLevel = level;
  88.  
  89.     gTotalFractals++;
  90.     gFractalChanged = true;
  91.     Microseconds(&startTime);
  92.     
  93.     gMaxX = 1 << level;
  94.     gMaxY = gMaxX / 2;
  95.     for (i = 0; i <= gMaxX; i++)           /* Clear the Array.  Use i & incrby as temps */
  96.         for (incrby = 0; incrby <= gMaxY; incrby++)
  97.             (*gNewPointArray)[i][incrby] = 0;
  98.  
  99.     for (i = 1; i <= level; i++) {
  100.         for (power = 1.0, j = 0; j < i; j++) {
  101.             if (gContourType == kStyleMountains)
  102.                 power *= 1.5 + (level / 40.0);    /* Mountains must be bumpier than the other styles */
  103.             else
  104.                 power *= 1.8 + (level / 40.0);
  105.         }
  106.         length = 10000 / power;            /* = 10000/(1.8^i) */
  107.         incrby = gMaxX / (1 << i);         /* # of line segments in a side of the triangle */
  108.         sk = incrby * 2;
  109.         CalcXs(length, incrby, sk);
  110.         CalcYs(length, incrby, sk);
  111.         CalcDiagonals(length, incrby, sk);
  112.     }
  113.     
  114.     Microseconds(&endTime);
  115.     if (startTime.hi == endTime.hi)
  116.         gMicrosecondCount += endTime.lo - startTime.lo;
  117.     else
  118.         gMicrosecondCount += startTime.lo - endTime.lo;
  119. }
  120.  
  121.  
  122. /*
  123.     CalcXs
  124.     
  125.     This function calculates the fractal values in the X direction.
  126.     It is called for each level and subdivides the existing edges
  127.     of the fractal to get the next level.
  128. */
  129. void CalcXs(long len, long incr, long sk)
  130. {
  131.     long             y, x;
  132.     long             d1, d2;
  133.  
  134.     for (y=0; y < gMaxX; y += sk) {
  135.         for (x = incr+y; x <= gMaxX; x += sk) {
  136.             d1 = GetZValue(x-incr, y);
  137.             d2 = GetZValue(x+incr, y);
  138.             SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
  139.         }
  140.     }
  141. }
  142.  
  143.  
  144. /*
  145.     CalcYs
  146.  
  147.     This function calculates the fractal values in the Y direction.
  148.     It is called for each level and subdivides the existing edges
  149.     of the fractal to get the next level.
  150. */
  151. void CalcYs(long len, long incr, long sk)
  152. {
  153.     long y, x;
  154.     long d1, d2;
  155.  
  156.     for (x = gMaxX; x >= 1; x -= sk)
  157.         for (y = incr; y <= x; y += sk) {
  158.             d1 = GetZValue(x, y + incr);
  159.             d2 = GetZValue(x, y - incr);
  160.             SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
  161.         }
  162. }
  163.  
  164.  
  165. /*
  166.     CalcDiagonals
  167.  
  168.     This function calculates the fractal values in the diagonal direction.
  169.     It is called for each level and subdivides the existing edges
  170.     of the fractal to get the next level.
  171. */
  172. void CalcDiagonals(long len, long incr, long sk)
  173. {
  174.     long             y, x;
  175.     long             d1, d2;
  176.         
  177.     for (x = 0; x < gMaxX; x += sk)
  178.     for (y = incr; y <= gMaxX - x; y += sk) {
  179.         d1 = GetZValue(x + y - incr, y - incr);
  180.         d2 = GetZValue(x + y + incr, y + incr);
  181.         SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x + y, y);
  182.     }
  183. }
  184.  
  185.  
  186. /*
  187.     GetZValue
  188.  
  189.     This function returns the Z value (i.e. the value stored in the
  190.     two-dimensional gPlotArray array. Because the fractal is triangular
  191.     and the array is square, we will store half the triange in the lower
  192.     part of the array the the other half in the upper part of the
  193.     array.
  194. */
  195. long GetZValue(long x, long y)
  196. {
  197.     if (y <= gMaxY)
  198.         return (*gNewPointArray)[x][y];
  199.     else
  200.         return (*gNewPointArray)[gMaxX - x][gMaxX + 1 - y];
  201. }
  202.  
  203.  
  204. /*
  205.     SetZValue
  206.  
  207.     This function saves the Z value (i.e. the value in the
  208.     two-dimensional gPlotArray array. Because the fractal is triangular
  209.     and the array is square, we will store half the triange in the lower
  210.     part of the array the the other half in the upper part of the
  211.     array.
  212. */
  213. void SetZValue(long d, long x, long y)
  214. {
  215.     if (y <= gMaxY)
  216.         (*gNewPointArray)[x][y] = d;
  217.     else
  218.         (*gNewPointArray)[gMaxX - x][gMaxX + 1 - y] = d;
  219. }
  220.  
  221.  
  222. /*
  223.     Transform1Point
  224.     
  225.     This function transforms a single XY point and stores
  226.     returns its XY coordinate as well as its scaled three-
  227.     space Z coordinate into the result structure.
  228. */
  229. void Transform1Point(long xIndex, long yIndex, ThreePoint *resultPoint)
  230. {
  231.     long    newComponent,
  232.             oldComponent;
  233.     long    componentShift;
  234.     long    xComponent, yComponent;
  235.     long    yResult, zResult;
  236.     
  237.     componentShift = kMaxLevel + 1 - gContourLevel;
  238.     xComponent = xIndex << componentShift;
  239.     yComponent = yIndex << componentShift;
  240.     
  241.     resultPoint->x = xComponent + yComponent + kXOrigin;
  242.     if (gContourType == kStyleMountains)
  243.         yResult = -xComponent + yComponent + kYMountainOrigin;
  244.     else
  245.         yResult = xComponent + -yComponent + kYOrigin;
  246.  
  247.     if (gMorphFractal) {
  248.         newComponent = (((yIndex <= gMaxY) ? (*gNewPointArray)[xIndex][yIndex] : 
  249.                 (*gNewPointArray)[gMaxX - xIndex][gMaxX + 1 - yIndex])) * gMorphAmount;
  250.     
  251.         oldComponent = (((yIndex <= gMaxY) ? (*gOldPointArray)[xIndex][yIndex] : 
  252.                 (*gOldPointArray)[gMaxX - xIndex][gMaxX + 1 - yIndex])) * gMorphAmountNeg;
  253.     
  254.         zResult = newComponent + oldComponent;
  255.     }
  256.     else {
  257.         zResult = (((yIndex <= gMaxY) ? (*gNewPointArray)[xIndex][yIndex] : 
  258.                 (*gNewPointArray)[gMaxX - xIndex][gMaxX + 1 - yIndex]));
  259.     }
  260.     
  261.     yResult -= (zResult >> kScaleShift);
  262.     resultPoint->y = yResult;
  263.     resultPoint->z = zResult;
  264. }
  265.  
  266.  
  267. /*
  268.     PlotData
  269.     
  270.     Our window is already open at this point, and it is cleared.
  271.     All we have to do now is fill it. This function draws the 
  272.     2D projection of the triangular database on the screen.
  273. */
  274. void PlotData(Boolean plotSurface)
  275. {
  276.     long             xindex, yindex;
  277.  
  278.     gMorphAmountNeg = (1.0 - gMorphAmount);
  279.     
  280.     if (plotSurface)
  281.         SetGrayValues();
  282.     
  283.     gPixelBase = (char*)GetPixBaseAddr(gOffscreenPixMap);
  284.     gRowBytesIncrement = (*gOffscreenPixMap)->rowBytes & 0x7FFF;
  285.  
  286.     gDBluePixelColor = GetLineColor(0, 0xFFFF, 0);
  287.     gLBluePixelColor = GetLineColor(0x3FFF, 0xFFFF, 0x3FFF);
  288.     gCurPixelColor = gBlackPixelColor = GetLineColor(0, 0, 0);
  289.  
  290.     if (!plotSurface) {
  291.         for (xindex = 1; xindex <= gMaxX; xindex++) {
  292.             for (yindex = 0; yindex <= xindex; yindex++)
  293.                 Transform1Point(xindex, yindex, &((*gPlotPointArray)[xindex][yindex]));
  294.         }
  295.  
  296.         for (xindex = 0; xindex < gMaxX; xindex++) {
  297.             for (yindex = xindex - 1; yindex >= 0; yindex--) {
  298.             
  299.                 ThreePoint     *currentPoint1,
  300.                             *currentPoint2,
  301.                             *currentPoint3;
  302.  
  303.                 currentPoint1 = &((*gPlotPointArray)[xindex][yindex]);
  304.                 currentPoint2 = &((*gPlotPointArray)[xindex + 1][yindex]);
  305.                 currentPoint3 = &((*gPlotPointArray)[xindex + 1][yindex + 1]);
  306.  
  307.                 if (gContourType == kStyleWater) {
  308.                     WaterPlotLine(currentPoint1, currentPoint2);
  309.                     WaterPlotLine(currentPoint2, currentPoint3);
  310.                     WaterPlotLine(currentPoint3, currentPoint1);
  311.                 }
  312.                 else {
  313.                     LineToOffscreen(currentPoint1->x, currentPoint1->y, currentPoint2->x, currentPoint2->y);
  314.                     LineToOffscreen(currentPoint2->x, currentPoint2->y, currentPoint3->x, currentPoint3->y);
  315.                     LineToOffscreen(currentPoint3->x, currentPoint3->y, currentPoint1->x, currentPoint1->y);
  316.                 }
  317.             }
  318.         
  319.         }
  320.     }
  321.     else {
  322.     
  323.         SetBlitBuffer((void *)gPixelBase, NULL, gRowBytesIncrement);
  324.     
  325.         for (xindex = 1; xindex <= gMaxX; xindex++) {
  326.             for (yindex = 0; yindex <= xindex; yindex++) {
  327.                 Transform1Point(xindex, yindex, &((*gPlotPointArray)[xindex][yindex]));
  328.             }
  329.         }
  330.  
  331.         for (xindex = 0; xindex < gMaxX; xindex++) {
  332.             for (yindex = xindex - 1; yindex >= 0; yindex--) {
  333.             
  334.                 ThreePoint        *currentPoint1,
  335.                                 *currentPoint2,
  336.                                 *currentPoint3;
  337.             
  338.                 long              vertex1H, vertex1V;
  339.                 long             vertex2H, vertex2V;
  340.                 long              vertex3H, vertex3V;
  341.                 long            diff1, diff2, diffTotal;
  342.                 unsigned char    color;
  343.                             
  344.                 currentPoint1 = &((*gPlotPointArray)[xindex][yindex]);
  345.                 
  346.                 vertex1H = currentPoint1->x;
  347.                 vertex1V = currentPoint1->y;
  348.                 
  349.                 currentPoint2 = &((*gPlotPointArray)[xindex + 1][yindex]);
  350.  
  351.                 vertex2H = currentPoint2->x;
  352.                 vertex2V = currentPoint2->y;
  353.  
  354.                 currentPoint3 = &((*gPlotPointArray)[xindex + 1][yindex + 1]);
  355.  
  356.                 vertex3H = currentPoint3->x;
  357.                 vertex3V = currentPoint3->y;
  358.                 
  359.                 color = 0;
  360.                 
  361.                 if (gContourType == kStyleWater) {
  362.                 
  363.                     if (currentPoint1->z <= 0) {
  364.                         vertex1V += currentPoint1->z >> kScaleShift;
  365.                         color = gDBluePixelColor;
  366.                     }
  367.                     
  368.                     if (currentPoint2->z <= 0) {
  369.                         vertex2V += currentPoint2->z >> kScaleShift;
  370.                         color = gDBluePixelColor;
  371.                     }
  372.  
  373.                     if (currentPoint3->z <= 0) {
  374.                         vertex3V += currentPoint3->z >> kScaleShift;
  375.                         color = gDBluePixelColor;
  376.                     }                
  377.                 
  378.                 }                
  379.         
  380.                 if (color != gDBluePixelColor) {
  381.  
  382.                     diff1 = currentPoint1->z - currentPoint2->z;
  383.                     if (diff1 < 0)
  384.                         diff1 = -diff1;
  385.                     
  386.                     diff2 = currentPoint3->z - currentPoint2->z;
  387.                     if (diff2 < 0)
  388.                         diff2 = -diff2;
  389.                     
  390.                     diffTotal = (diff1 + diff2) >> (kMaxLevel + 3 - gContourLevel);
  391.     
  392.                     color =  gGrayColors[gShadeMap[ConvertToShadeIndex(diffTotal)]];
  393.  
  394.                 }
  395.                                 
  396.                 FillTriangle(vertex1H, vertex1V,
  397.                              vertex2H, vertex2V,
  398.                              vertex3H, vertex3V,
  399.                              color);
  400.                 
  401.                 if (yindex < xindex - 1) {
  402.                 
  403.                     currentPoint2 = &((*gPlotPointArray)[xindex][yindex + 1]);
  404.                     
  405.                     vertex2H = currentPoint2->x;
  406.                     vertex2V = currentPoint2->y;
  407.  
  408.                     color = 0;
  409.  
  410.                     if (gContourType == kStyleWater) {
  411.                                             
  412.                         if (currentPoint1->z <= 0)
  413.                             color = gLBluePixelColor;
  414.  
  415.                         if (currentPoint2->z <= 0) {
  416.                             vertex2V += currentPoint2->z >> kScaleShift;
  417.                             color = gLBluePixelColor;
  418.                         }
  419.     
  420.                         if (currentPoint3->z <= 0)
  421.                             color = gLBluePixelColor;
  422.  
  423.                     }
  424.  
  425.                     if (color != gLBluePixelColor) {
  426.                     
  427.                         diff1 = currentPoint1->z - currentPoint2->z;
  428.                         if (diff1 < 0)
  429.                             diff1 = -diff1;
  430.                         
  431.                         diff2 = currentPoint3->z - currentPoint2->z;
  432.                         if (diff2 < 0)
  433.                             diff2 = -diff2;
  434.                         
  435.                         diffTotal = (diff1 + diff2) >> (kMaxLevel + 3 - gContourLevel);
  436.     
  437.                         color = gGrayColors[gShadeMap[ConvertToShadeIndex(diffTotal)]];
  438.                         
  439.                     }
  440.  
  441.                     FillTriangle(vertex1H, vertex1V,
  442.                              vertex2H, vertex2V,
  443.                              vertex3H, vertex3V,
  444.                              color);
  445.  
  446.                 }
  447.             }
  448.         }
  449.     }
  450. }
  451.  
  452.  
  453. /*
  454.     WaterPlotLine
  455. */
  456. void WaterPlotLine(ThreePoint *startPoint, ThreePoint  *endPoint)
  457. {
  458.     if (startPoint->z <= 0) {
  459.         PointOffscreen(startPoint->x, startPoint->y + (startPoint->z >> kScaleShift), gDBluePixelColor);
  460.         if (endPoint->z <= 0)
  461.             PointOffscreen(endPoint->x, endPoint->y + (endPoint->z >> kScaleShift), gDBluePixelColor);
  462.     }
  463.     else if (endPoint->z <= 0)
  464.         PointOffscreen(endPoint->x, endPoint->y + (endPoint->z >> kScaleShift), gDBluePixelColor);
  465.     else
  466.         LineToOffscreen(startPoint->x, startPoint->y, endPoint->x, endPoint->y);
  467. }
  468.  
  469.  
  470. /*
  471.     GetLineColor
  472.     
  473.     This function returns the pixel value for the given
  474.     RGB color.
  475. */
  476. unsigned char GetLineColor(unsigned short red, unsigned short blue, unsigned short green)
  477. {
  478.     RGBColor        curColor;
  479.  
  480.     curColor.red = red;
  481.     curColor.blue = blue;
  482.     curColor.green = green;
  483.     return Color2Index(&curColor);
  484. }
  485.  
  486.  
  487. /*
  488.     SetGrayValues
  489. */
  490. void SetGrayValues(void)
  491. {
  492.     long        curGrayIndex;
  493.     long        curGrayValue;
  494.     long        grayIncrement;
  495.     RGBColor    curColor;
  496.     
  497.     grayIncrement = (1<<15) / (kTotalGrays - 1);
  498.     curGrayValue = 32767;
  499.     for (curGrayIndex = 0; curGrayIndex < kTotalGrays; curGrayIndex++) {
  500.         curColor.red = curColor.blue = curColor.green = curGrayValue;
  501.         gGrayColors[curGrayIndex] = Color2Index(&curColor);
  502.         curGrayValue += grayIncrement;
  503.     }
  504. }
  505.  
  506.  
  507. /*
  508.     LineToOffscreen
  509.     
  510.     This function replaces the LineTo function with a custom
  511.     off-screen line drawing function.
  512. */
  513. void LineToOffscreen(long sX, long sY, long eX, long eY)
  514. {
  515.     if (AbsoluteValue(eY - sY) > AbsoluteValue(eX - sX)) {
  516.         if (eY < sY)
  517.             LineYOffscreen(eX, eY, sX, sY);
  518.         else
  519.             LineYOffscreen(sX, sY, eX, eY);
  520.     }
  521.     else {
  522.         if (eX < sX)
  523.             LineXOffscreen(eX, eY, sX, sY);
  524.         else
  525.             LineXOffscreen(sX, sY, eX, eY);
  526.     }
  527. }
  528.  
  529.  
  530. /*
  531.     LineXOffscreen
  532.  
  533.     Draws lines with a slope between -1 and 1
  534. */
  535. void LineXOffscreen(long startX, long startY, long endX, long endY)
  536. {
  537.     char*            pixelAddress;
  538.     unsigned char    localColor = gCurPixelColor;
  539.     long            localRowBytes = gRowBytesIncrement;
  540.     long            currentX;
  541.     
  542.     if (endX == startX) {
  543.         pixelAddress = gPixelBase + startX + (unsigned long)(startY * localRowBytes);
  544.         for (currentX = startX; currentX <= endX; currentX++)
  545.             *pixelAddress++ = localColor;    
  546.     }
  547.     else {
  548.         long            newY;
  549.         long            slope, currentY;
  550.         
  551.         slope = ((endY - startY) << 16) / (endX - startX);
  552.  
  553.         pixelAddress = gPixelBase + startX + (unsigned long)(startY * localRowBytes) - 1;
  554.         currentY = (startY << 16) + (1 << 15);
  555.         
  556.         if (endY > startY) {
  557.             for (currentX = startX; currentX <= endX; currentX++, currentY = newY) {
  558.                 newY = currentY + slope;
  559.                 *++pixelAddress = localColor;
  560.                 if ((newY >> 16) > (currentY >> 16))
  561.                     pixelAddress += localRowBytes;
  562.             }
  563.         }
  564.         else {
  565.             for (currentX = startX; currentX <= endX; currentX++, currentY = newY) {
  566.                 newY = currentY + slope;
  567.                 *++pixelAddress = localColor;
  568.                 if ((currentY >> 16) > (newY >> 16))
  569.                     pixelAddress -= localRowBytes;
  570.             }
  571.         }
  572.     }
  573. }
  574.  
  575.  
  576. /*
  577.     LineYOffscreen
  578.  
  579.     Draws lines with a slope <= -1 or >= 1
  580. */
  581. void LineYOffscreen(long startX, long startY, long endX, long endY)
  582. {
  583.     long            currentY;
  584.     char*            pixelAddress;
  585.     unsigned char    localColor = gCurPixelColor;
  586.     long            localRowBytes = gRowBytesIncrement;
  587.     
  588.     if (endY == startY) {
  589.         pixelAddress = gPixelBase + startX + (unsigned long)(startY * localRowBytes);
  590.         for (currentY = startY; currentY <= endY; currentY++) {
  591.             *pixelAddress = localColor;
  592.             pixelAddress += localRowBytes;
  593.         }
  594.     }
  595.     else {
  596.         long            newX;
  597.         long            slope, currentX;
  598.         
  599.         slope = ((endX - startX) << 16) / (endY - startY);
  600.  
  601.         pixelAddress = gPixelBase + startX + (unsigned long)(startY * localRowBytes);
  602.         currentX = (startX << 16) + (1 << 15);
  603.         
  604.         if (endX > startX) {
  605.             for (currentY = startY; currentY <= endY; currentY++, currentX = newX) {
  606.                 newX = currentX + slope;
  607.                 *pixelAddress = localColor;
  608.                 pixelAddress += localRowBytes;
  609.                 if ((newX >> 16) - (currentX >> 16))
  610.                     pixelAddress++;
  611.             }
  612.         }
  613.         else {
  614.             for (currentY = startY; currentY <= endY; currentY++, currentX = newX) {
  615.                 newX = currentX + slope;
  616.                 *pixelAddress = localColor;
  617.                 pixelAddress += localRowBytes;
  618.                 if ((currentX >> 16) - (newX >> 16))
  619.                     pixelAddress--;
  620.             }
  621.         }
  622.     }
  623. }
  624.  
  625.  
  626. /*
  627.     PointOffscreen
  628.  
  629.     Draws a single pixel
  630. */
  631. void PointOffscreen(long x, long y , unsigned char colorValue)
  632. {
  633.     char*            pixelAddress;
  634.  
  635.     pixelAddress = gPixelBase + x;
  636.     pixelAddress += (unsigned long)(y * gRowBytesIncrement);
  637.     *pixelAddress = colorValue;
  638. }
  639.  
  640.  
  641.  
  642.  
  643.  
  644.  
  645.  
  646.